#include "stdafx.hpp"
#include "ColligereAudioDatabaseInterface.hpp"
#include "ColligereDatabaseError.hpp"
#include "ColligereConfigHandler.hpp"
#include "ColligereOrangeCdXmlDataImporter.hpp"
#include "ColligereCatraxxXmlDataImporter.hpp"
#include "ColligereColligereXmlDataImporter.hpp"
#include "ColligereDiscogsDataImporter.hpp"
#include "ColligereXmlDataExporter.hpp"
#include "ColligereIds.hpp"

col::AudioDatabaseInterface::AudioDatabaseInterface() : mpDbLayout(0)
{
}

col::AudioDatabaseInterface::~AudioDatabaseInterface()
{
}

void col::AudioDatabaseInterface::Open ( const wxString &rDbPath,
										wxAuiManager &rParentLayout )
{
	try
	{
		mpDbLayout = new AudioDatabaseLayout 
			( rParentLayout.GetManagedWindow(), this );
		mpDbLayout->Hide();
		mpDbLayout->Create();

		mpParentLayout = &rParentLayout;

		mpParentLayout->AddPane ( mpDbLayout, wxCENTER );
		mpParentLayout->GetPane ( mpDbLayout ).Name ( "Layout" );

		long sync_type = -1;
		ConfigHandler::Get().GetValue ( ConfigVariable::DatabaseSyncType, sync_type );
		wxASSERT ( sync_type != -1 );

		mDbHandler.SetParentEvtHandler ( mpDbLayout->GetEventHandler() );
		mDbHandler.Open ( rDbPath, sync_type,
			ConfigHandler::Get().IsEnabled ( ConfigVariable::AutoVacuumDatabase ) );

		if ( mDbHandler.IsQueryable() )
		{
			mDbHandler.QueryAllItems();
		}

		typedef AudioDatabaseTables t;
		typedef ControlLink cl;

		DatabaseTable *artists = DatabaseTableListHandler::Get().GetTable 
			( mDbHandler.GetTypeId(),"artists" );
		wxASSERT ( artists );

		artists->GetColumn ( t::Artists::Name ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Artist" ) );
		artists->GetColumn ( t::Artists::Country ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Country" ) );
		artists->GetColumn ( t::Artists::Homepage ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Homepage" ) );

		DatabaseTable *albums = DatabaseTableListHandler::Get().GetTable 
			( mDbHandler.GetTypeId(),"albums" );
		wxASSERT ( albums );

		albums->GetColumn ( t::Albums::Title ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Album" ) );
		albums->GetColumn ( t::Albums::Year ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Year" ) );
		albums->GetColumn ( t::Albums::Genre ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Genre" ) );
		albums->GetColumn ( t::Albums::Format ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Format" ) );
		albums->GetColumn ( t::Albums::Condition ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Condition" ) );
		albums->GetColumn ( t::Albums::Status ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Status" ) );

		albums->GetColumn ( t::Albums::ExtInfo ).SetControlLink ( cl 
			( "ID_TextCtrl_Notes" ) );
		
		albums->GetColumn ( t::Albums::Label ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Label" ) );
		albums->GetColumn ( t::Albums::CatalogNum ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "CatalogNum" ) );
		albums->GetColumn ( t::Albums::Rating ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Rating" ) );

		albums->GetColumn ( t::Albums::Tags ).SetControlLink ( cl 
			( "ID_TextCtrl_Tags" ) );

		albums->GetColumn ( t::Albums::PurchasePrice ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Purchase Price" ) );
		albums->GetColumn ( t::Albums::SellPrice ).SetControlLink ( cl 
			( ID_PropertyGrid_ReleaseInfo, "Sell Price" ) );

		DatabaseTable *tracks = DatabaseTableListHandler::Get().GetTable 
			( mDbHandler.GetTypeId(),"tracks" );
		wxASSERT ( tracks );

		tracks->GetColumn ( t::Tracks::Num ).SetControlLink ( cl 
			( "ID_ListCtrl_Tracks", 0 ) );
		tracks->GetColumn ( t::Tracks::Title ).SetControlLink ( cl 
			( "ID_ListCtrl_Tracks", 1 ) );
		tracks->GetColumn ( t::Tracks::Length ).SetControlLink ( cl 
			( "ID_ListCtrl_Tracks", 2 ) );

		SetActive ( this );
		mpDbLayout->Show();
	}
	catch ( DatabaseError &rErr )
	{
		wxLogError ( rErr.what() );
		throw;
	}
}

col::DatabaseProperties col::AudioDatabaseInterface::GetProperties()
{
	return DatabaseProperties ( *this );
}

void col::AudioDatabaseInterface::Save ( const wxString &rNewDbPath )
{
	try
	{
		long compression_level = 1;

		ConfigHandler::Get().GetValue 
			( ConfigVariable::DatabaseCompressionLevel, compression_level );

		mDbHandler.Save ( rNewDbPath, compression_level );
	}
	catch ( DatabaseError &rErr )
	{
		wxLogError ( rErr.what() );
		wxLogStatus ( _("Database Saving Failed") );
	}
}

bool col::AudioDatabaseInterface::Close()
{
	using namespace boost;

	if ( mDbHandler.GetChangedItemCount() > 0 )
	{
		if ( ConfigHandler::Get().IsEnabled ( ConfigVariable::SaveDatabaseOnClose ) )
		{
			Save();
		}
		else
		{
			wxInt32 answer = wxMessageBox 
				( str ( format ( _( "Save changes to \"%1%\"?" ) ) % 
				mDbHandler.GetInputFile() ), _("Save?"), wxICON_QUESTION | 
				wxYES_NO | wxCANCEL, mpDbLayout );

			if ( answer == wxCANCEL ) 
			{
				return false;
			}
			else if ( answer == wxYES )
			{
				Save();
			}
		}
	}
	try
	{
		DatabaseEventBase close_evt ( COLLIGERE_EVT_DATABASE_USER_REQUEST_CLOSE, GetId() );
		mpDbLayout->GetEventHandler()->ProcessEvent ( close_evt );

		mpDbLayout->Hide();
		mpParentLayout->DetachPane ( mpDbLayout );
		mDbHandler.Close();
		mpDbLayout->Destroy();
		
		SetActive(0);
	}
	catch ( DatabaseError &rErr )
	{
		wxLogError ( rErr.what() );
	}
	return true;
}

bool col::AudioDatabaseInterface::IsOpened() const
{
	return mDbHandler.IsOpened();
}

col::AudioDatabaseLayout *col::AudioDatabaseInterface::GetLayout()
{
	return mpDbLayout;
}

col::AudioDatabaseHandler *col::AudioDatabaseInterface::GetHandler()
{
	return &mDbHandler;
}

const col::AudioDatabaseLayout *col::AudioDatabaseInterface::GetLayout() const
{
	return mpDbLayout;
}

const col::AudioDatabaseHandler *col::AudioDatabaseInterface::GetHandler() const
{
	return &mDbHandler;
}


wxUint32 col::AudioDatabaseInterface::GetId() const
{
	return mDbHandler.GetId();
}

void col::AudioDatabaseInterface::DispatchGuiEvent ( wxUint32 eventId )
{
	switch ( eventId )
	{
	case ID_Menu_AddItem:
		{
			AddItem();
			break;
		}
	case wxID_DELETE:
		{
			TrashItem();
			break;
		}
	case wxID_SAVE:
		{
			Save();
			break;
		}
	case wxID_SAVEAS:
		{
			wxFileDialog dlg ( mpDbLayout, _("Choose a file"), "", 
				"", _( "COL Files (*.col)|*.col|All Files (*.*)|*.*" ),
				wxFD_SAVE | wxFD_OVERWRITE_PROMPT );

			if ( dlg.ShowModal() == wxID_OK )
			{
				Save ( dlg.GetPath() );
			}
			break;
		}
	case wxID_CLEAR:
		{
			mpDbLayout->ClearInputControls();
			break;
		}
	case wxID_COPY:
		{
			CopyItem();
			break;
		}
	case wxID_PASTE:
		{
			PasteItem();
			break;
		}
	case ID_Menu_DeselectItem:
		{
			mpDbLayout->UnselectActiveItem();
			break;
		}
	case ID_Menu_UpdateItem:
		{
			UpdateItem();
			break;
		}
	case ID_Menu_SaveLayout:
		{
			mpDbLayout->SaveLayout();
			break;
		}
	case ID_Menu_ViewItems:
		{
			mpDbLayout->ShowDatabaseItems ( !mpDbLayout->IsDatabaseItemListShown() );
			break;
		}
	case ID_Menu_ViewRecycleBin:
		{
			mpDbLayout->ShowRecycleBin ( !mpDbLayout->IsRecycleBinShown() );
			break;
		}
	case ID_Menu_ImportOrangeCdXml:
	case ID_Menu_ImportCatraxxXml:
	case ID_Menu_ImportColligereXml:
	case ID_Menu_ImportDiscogs:
		{
			Import ( eventId );
			break;
		}
	case ID_Menu_ExportToXml:
		{
			Export ( eventId );
			break;
		}
	case ID_Menu_SearchFreeDb:
		{
			mpDbLayout->ShowSearchFreeDb ( !mpDbLayout->IsSearchFreeDbShown() );
			break;
		}
	case ID_Menu_SearchDiscogs:
		{
			mpDbLayout->ShowSearchDiscogs ( !mpDbLayout->IsSearchDiscogsShown() );
			break;
		}
	default:
		break;
	}
}

const wxString col::AudioDatabaseInterface::GetStatusText
( wxUint32 statBarColumn ) const
{
	wxASSERT ( IsOpened() );
	return mpDbLayout->GetStatusText ( statBarColumn );
}

const wxString col::AudioDatabaseInterface::GetTitleText() const
{
	wxASSERT ( IsOpened() );
	return mpDbLayout->GetTitleText();
}

void col::AudioDatabaseInterface::UpdateItem()
{
	wxASSERT ( IsOpened() );
	wxInt32 answer = wxYES;

	if ( ConfigHandler::Get().IsEnabled ( ConfigVariable::AskBeforeApplyingValues ) )
	{
		answer = wxMessageBox ( _( "Update new values to the selected item?" ), 
			_("Update?"), wxICON_QUESTION | wxYES_NO, mpDbLayout );
	}
	if ( answer == wxYES )
	{
		try
		{
			wxWindowUpdateLocker lock_layout ( mpDbLayout );

			const Artist old_values ( dynamic_cast < const Artist & > 
				( mpDbLayout->GetPrevParsedItem() ) );

			Artist new_values ( mpDbLayout->GetItemFromInputControls() );

			const col::AlbumTreeItemData &sel_item = dynamic_cast 
				< const col::AlbumTreeItemData & > 
				( *mpDbLayout->GetSelectedItemData() );

			if ( old_values == new_values )
			{
				new_values.SetId ( sel_item.GetArtistId() );
				Album new_album ( *new_values.GetFirstChild() );
				new_album.SetId ( sel_item.GetAlbumId() );
				new_values.ClearChildren();
				new_values.AddChild ( new_album );
				mDbHandler.UpdateItem ( new_values );
			}
			else
			{
				mDbHandler.AddItem ( new_values );
				mDbHandler.DeleteItem ( *old_values.GetLastChild() );
			}
			wxLogStatus ( _("Album Updated") );
		}
		catch ( std::bad_cast &rE )
		{
			wxFAIL_MSG ( rE.what() );
		}
	}
}

void col::AudioDatabaseInterface::AddItem()
{
	wxASSERT ( IsOpened() );

	mDbHandler.AddItem ( mpDbLayout->GetItemFromInputControls() );
	mpDbLayout->ClearInputControls();
	
	wxLogStatus ( _("Album Added") );
}

void col::AudioDatabaseInterface::TrashItem()
{
	wxASSERT ( IsOpened() );

	const TreeItemData *data = mpDbLayout->GetSelectedItemData();

	wxASSERT_MSG ( data, "No item selected!" );

	if ( data )
	{
		if ( const AlbumTreeItemData *album_data = dynamic_cast 
			< const AlbumTreeItemData * > ( data ) )
		{
			wxLogDebug ( "Trashing the album with the ID %d (ParentID: %d).", 
				album_data->GetAlbumId(), album_data->GetArtistId() );

			mDbHandler.SetAlbumAsTrashed ( album_data->GetArtistId(), 
				album_data->GetAlbumId() );	

			wxLogStatus ( _("Album Trashed") );
		}
		else if ( const ArtistTreeItemData *artist_data = dynamic_cast 
			< const ArtistTreeItemData * > ( data ) )
		{
			wxLogDebug ( "Trashing all albums from the artist with the ID %d.", 
				artist_data->GetArtistId() );
			mDbHandler.SetArtistAsTrashed ( artist_data->GetArtistId() );
		}
	}
}

void col::AudioDatabaseInterface::CopyItem()
{
	wxASSERT ( IsOpened() );
	mCurCopied.reset( mpDbLayout->GetItemFromInputControls().Clone() );
}

void col::AudioDatabaseInterface::PasteItem()
{
	wxASSERT ( IsOpened() && mCurCopied );
	mDbHandler.AddItem ( *mCurCopied );
}

void col::AudioDatabaseInterface::Export ( wxUint32 exporterId )
{
	using namespace boost;

	wxASSERT ( IsOpened() );

	wxFileDialog dlg ( mpDbLayout, _("Choose a file"), empty_string, 
		empty_string, _( "XML Files (*.xml)|*.xml|All Files (*.*)|*.*"),
		wxFD_SAVE | wxFD_OVERWRITE_PROMPT );

	if ( dlg.ShowModal() != wxID_OK )
	{
		return;
	}

	wxStopWatch sw;

	try
	{
		switch ( exporterId )
		{
		case ExporterId::Xml:
			{
				scoped_ptr < DataExporterBase > exporter ( new XmlDataExporter );
				exporter->Export ( *this, dlg.GetPath() );
				break;
			}
		default:
			break;
		}
		wxLogGeneric ( LogPerformance, _("Exporting took %ldms."), sw.Time() );
	}
	catch ( std::exception &rE )
	{
		wxLogError ( rE.what() );
	}
}

void col::AudioDatabaseInterface::Import ( const wxString &rFile )
{
	const wxFileName fn ( rFile );

	if ( fn.GetExt() == "oxl" )
		Import ( ImporterId::OrangeCd, rFile );
}

void col::AudioDatabaseInterface::Import ( wxUint32 importerId,
										  const wxString &rInFile )
{
	using namespace boost;

	wxASSERT ( IsOpened() );

	wxString uri;

	if ( importerId != ImporterId::Discogs ) 
	{
		wxString in_file ( rInFile );

		if ( in_file.empty() )
		{
			wxFileDialog dlg ( mpDbLayout, _("Choose a file"), empty_string, 
				empty_string, _( "All Files (*.*)|*.*|OXL Files (*.oxl)|*.oxl|"\
				"XML Files (*.xml)|*.xml"), wxFD_OPEN | wxFD_FILE_MUST_EXIST );

			if ( dlg.ShowModal() != wxID_OK )
			{
				return;
			}
			in_file = dlg.GetPath();
		}
		uri = wxString::Format ( "file://%s", in_file );
	}
	else
	{
		wxTextEntryDialog dlg ( mpDbLayout, "Username:", "Please Enter Your Discogs Username" );
		if ( !dlg.ShowModal() )
		{
			return;
		}
		uri = wxString::Format ( "%s/collection?user=%s", 
			SearchDiscogs::GetServerAddress(), dlg.GetValue() );
	}

	wxWindowUpdateLocker upd_lock ( mpDbLayout );

	wxStopWatch sw;

	try
	{
		switch ( importerId )
		{
		case ImporterId::Catraxx:
			{
				scoped_ptr < DataImporterBase > importer ( new CatraxxXmlDataImporter );
				importer->Load ( uri );
				importer->Import ( *this );
				break;
			}
		case ImporterId::OrangeCd:
			{
				scoped_ptr < DataImporterBase > importer ( new OrangeCdXmlDataImporter );
				importer->Load ( uri );
				importer->Import ( *this );
				break;
			}
		case ImporterId::Colligere:
			{
				scoped_ptr < DataImporterBase > importer ( new ColligereXmlDataImporter );
				importer->Load ( uri );
				importer->Import ( *this );
				break;
			}
		case ImporterId::Discogs:
			{
				scoped_ptr < DataImporterBase > importer ( new DiscogsDataImporter );
				importer->Load ( uri );
				importer->Import ( *this );
				break;
			}
		default:
			wxFAIL;
			break;
		}
		wxLogGeneric ( LogPerformance, _("Importing took %ldms."), sw.Time() );
	}
	catch ( DataImporterError &rE )
	{
		wxLogError ( rE.what() );
	}
	catch ( std::exception &rE )
	{
		wxLogError ( "%s", rE.what() );
	}
}